#include "stdafx.h"
#include "HttpException.h"
#include "isapi_python.h"
#include "WorkerThreadPool.h"
#include "HttpContext.h"
#include "WsgiResponse.h"
#include "PythonApiHelpers.h"
#include "WsgiInputStream.h"
#include "WsgiErrorStream.h"
#include "Configuration.h"

using namespace std;
using namespace ISAPI_Python::Win32Helpers;
using namespace boost::algorithm;

WorkerThreadPool* ThreadPool;

DWORD FASTCALL ProcessPythonRequest(LPVOID threadData) {
	{
		Context_ptr context(new HttpContext((LPEXTENSION_CONTROL_BLOCK)threadData));
		HttpResponse* response = context->get_Response();

		try {
			string fileName = context->GetServerVariable("PATH_TRANSLATED");
			string virtualPath = context->GetServerVariable("PATH_INFO");
			
			response->ExecuteWsgiScript(fileName, virtualPath);			
		} catch (HttpException& ex) {
			string errorMessage(ex.what());

			Logger_ptr logger = Logger::GetLogger(LOG_NAME);
			logger->Error(errorMessage);

			ostringstream status;
			status << ex.get_StatusCode();
			status << ' ';
			status << ex.get_StatusMessage();

			response->set_Status(status.str());
			response->set_ContentLength(errorMessage.length());
			response->Write(errorMessage);
		} catch (exception& ex) {
			string errorMessage(ex.what());

			Logger_ptr logger = Logger::GetLogger(LOG_NAME);
			logger->Error(errorMessage);

			string status("500 Internal Server Error");

			response->set_Status(status);
			response->set_ContentLength(errorMessage.length());
			response->Write(errorMessage);
		}

		response->End();
	}

	//_CrtDumpMemoryLeaks();

	return 0;
}

void InitPython(string& pythonPath) {
	if(pythonPath.size() <= 0) {
		Logger_ptr logger = Logger::GetLogger(LOG_NAME);
		logger->Warning("PythonPath configuration variable was not set.");
	}

	Py_Initialize();
	Py_InitModule(WsgiResponseModule, WsgiResponseMethods); //Add Python callback methods.

	vector<string> splitString;
	split(splitString, pythonPath, is_any_of(","));

	PyEval_InitThreads(); //This not only inits threading, but also acquires the GIL.
	{
		Python_ptr pathList(PyList_New(splitString.size()));

		//Initialize the PYTHONPATH.
		for(vector<string>::iterator iter = splitString.begin(); iter != splitString.end(); ++iter) {
			//Don't release these Python strings. PyList_SET_ITEM does
			// not increment their reference count when adding them to the
			// list.
			PyObject* path1 = PyString_FromString(iter->c_str());// "C:\\Python26\\DLLs");
			PyList_SET_ITEM(pathList.get(), 0, path1);	
		}

		Python_ptr sysModule(PyImport_ImportModule("sys"));
		PyObject_SetAttrString(sysModule.get(), "path", pathList.get());

		// You have to "finalize" any custom type you make with PyType_Ready(),
		// or they won't have any metadata attached to them.
		PyType_Ready(&InputStreamType);
		PyType_Ready(&ErrorStreamType);
	} //Scope created to ensure Python_ptr's get released before the GIL.

	PyEval_ReleaseLock(); //I don't want the GIL right now.
}


BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer) {
	//_crtBreakAlloc = 124;
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
	
	pVer->dwExtensionVersion = PY_VERSION_HEX;
	sprintf_s(pVer->lpszExtensionDesc, HSE_MAX_EXT_DLL_NAME_LEN, "Python %s", PY_VERSION);

	ThreadPool = new WorkerThreadPool();

	ifstream configFileStream("isapi_python.yaml");
	Configuration Config(configFileStream);
	string pythonPath = Config.PythonPath();
	InitPython(pythonPath);

	return TRUE;
}

DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) {
	ThreadPool->QueueWorkItem((WORKER_THREAD_CALLBACK)ProcessPythonRequest, lpECB);

	return HSE_STATUS_PENDING;
}

BOOL WINAPI TerminateExtension(DWORD dwFlags) {
	UNREFERENCED_PARAMETER(dwFlags);

	Py_Finalize();
	delete ThreadPool;
	return TRUE;
}
